<?php

namespace Authorization\Service;

use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
use Authorization\Service\AuthorizationService;
use Zend\View\Model\ViewModel;

class RouteListener implements ListenerAggregateInterface {

    protected $listeners = array();

    /**
     * @var AuthorizationService
     */
    protected $authorizationService;

    public function attach(EventManagerInterface $events) {
        $this->listeners[] = $events->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'), -1000);
        $this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($this, 'onUnauthorizedAccess'));
    }

    public function detach(EventManagerInterface $events) {
        foreach ($this->listeners as $index => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }

    public function onRoute(MvcEvent $e) {
        $app = $e->getTarget();
        $routeMatch = $e->getRouteMatch();
        $controller = $routeMatch->getParam('controller');
        $action = $routeMatch->getParam('action');
        $authorizationService = $this->authorizationService;

        if (!$authorizationService->isGranted($controller, $action)) {
            $e->setError($authorizationService::ERROR_UNAUTHORIZED);
            $app->getEventManager()->trigger(MvcEvent::EVENT_DISPATCH_ERROR, $e);
        }
    }

    public function onUnauthorizedAccess(MvcEvent $e) {
        $authorizationService = $this->authorizationService;

        $error = $e->getError();
        if (empty($error)) {
            return; // no error
        }

        if ($error !== $authorizationService::ERROR_UNAUTHORIZED) {
            return; // not authorization error
        }

        $result = $e->getResult();
        if ($result instanceof Response) {
            return; // error already handled
        }

        $response = $e->getResponse();
        if (!$response) {
            $response = new HttpResponse();
            $e->setResponse($response);
        }

        // if unauthorized -> show login form
        if ($this->authorizationService->isAnonymous()) {
            $router = $e->getRouter();
            $url = $router->assemble(array('action' => 'login'), array('name' => 'auth'));

            $response->getHeaders()->addHeaderLine('Location', $url);
            $response->setStatusCode(302);
            return;
        }

        // show error page
        $model = new ViewModel(array(
            'error' => $e->getParam('error'),
        ));
        $model->setTemplate('authorization/unauthorized');
        $e->setResult($model);
        $response->setStatusCode(403);
    }

    public function setAuthorizationService(AuthorizationService $authorizationService) {
        $this->authorizationService = $authorizationService;
    }
}
